<?php
// +----------------------------------------------------------------------
// | Library for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2021 青海云音信息技术有限公司 [ http://www.yyinfos.com ]
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// +----------------------------------------------------------------------
// | gitee 代码仓库：https://gitee.com/qhweb/ThinkAdmin
// +----------------------------------------------------------------------

namespace qhweb\service;

use think\migration\Migrator;
use qhweb\Library;

/**
 * 自定义表单服务
 * Class DiyService
 * @package qhweb\service
 */

class DiyService
{
    /**
     * 自定义模型所支持的字段类型
     * @var array
     */
    public static $modelFieldType = [
        'text'          => ['name' => '单行文本','field_list' => ['varchar', 'char','int', 'tinyint', 'smallint']],
        'textarea'      => ['name' => '多行文本','field_list' => ['varchar', 'text']],
        'richtext'      => ['name' => '富文本','field_list' => ['text', 'tinytext', 'mediumtext', 'longtext']],
        'number'        => ['name' => '数字','field_list' => ['int', 'tinyint', 'smallint', 'mediumint', 'integer', 'bigint', 'real', 'double', 'float', 'decimal', 'numeric']],
        'date'          => ['name' => '日期','field_list' => ['date', 'char', 'varchar']],
        'daterange'     => ['name' => '日期带范围','field_list' => ['varchar', 'char']],
        'time'          => ['name' => '时间','field_list' => ['time', 'char', 'varchar']],
        'timerange'     => ['name' => '时间带范围','field_list' => ['varchar', 'char']],
        'datetime'      => ['name' => '日期时间','field_list' => ['int', 'timestamp', 'datetime']],
        'datetimerange' => ['name' => '日期时间带范围','field_list' => ['varchar', 'char']],
        'select'        => ['name' => 'select下拉框','field_list' => ['varchar', 'char', 'int', 'tinyint']],
        'radio'         => ['name' => 'radio单选按钮','field_list' => ['varchar', 'char', 'int', 'tinyint']],
        'checkbox'      => ['name' => 'checkbox复选按钮', 'field_list' => ['varchar', 'char']],
        'image'         => ['name' => '单图片','field_list' => ['varchar', 'char']],
        'images'        => ['name' => '多图片', 'field_list' => ['varchar', 'text', 'tinytext', 'mediumtext', 'longtext']],
        'file'          => ['name' => '单文件','field_list' => ['varchar', 'char']],
        'files'         => ['name' => '多文件','field_list' => ['varchar', 'text', 'tinytext', 'mediumtext', 'longtext']],
        'color_select'  => ['name' => '颜色选择器','field_list' => ['char', 'varchar']],
        'slider'        => ['name' => '滑块','field_list' => ['char', 'varchar', 'int', 'tinyint']],
        'score'         => ['name' => '评分','field_list' => ['char', 'varchar', 'int', 'tinyint']],
    ];
    /**
     * 模型字段内置的正则表达式
     *
     * @var array
     */
    public static $modelPattern = [
        'text'          => ['name' => '普通字符','rule' => '/^[.*]+$/'],
        'number'        => ['name' => '数字','rule' => '/^[0-9.-]+$/'],
        'intger'        => ['name' => '整数','rule' => '/^[0-9-]+$/'],
        'letter'        => ['name' => '字母','rule' => '/^[a-z]+$/i'],
        'letter_number' => ['name' => '数字+字母','rule' => '/^[0-9a-z]+$/i'],
        'email'         => ['name' => 'Email','rule' => '/^[\w\-\.]+@[\w\-\.]+(\.\w+)+$/'],
        'QQ'            => ['name' => 'QQ','rule' => '/^[0-9]{5,20}$/'],
        'link'          => ['name' => '超链接','rule' => '/[a-zA-z]+://[^\s]*/'],
        'phone'         => ['name' => '手机号码','rule' => '/^(1)[0-9]{10}$/'],
        'tel'           => ['name' => '电话号码','rule' => '/^[0-9-]{6,13}$/'],
        'post_code'     => ['name' => '邮政编码','rule' => '/^[0-9]{6}$/'],
        'id_card'       => ['name' => '身份证号','rule' => '/^\d{17}[\d|x]|\d{15}$/'],
        'chstr'         => ['name' => '中文字符','rule' => '/^[^\x00-\xff]$/']
    ];
    /**
     * 字段类型属性
     * @var array
     */
    public static $settingField = [
        'text'          => 'chartype,length,min,max,defaultvalue,is_password,width,css',
        'textarea'      => 'chartype,length,defaultvalue,width,height,css,required',
        'richtext'      => 'chartype,defaultvalue,width,height,required',
        'number'        => 'chartype,length,defaultvalue,width,css,required',
        'date'          => 'chartype,length,defaultvalue,width,css,required',
        'daterange'     => 'chartype,length,defaultvalue,width,css,required',
        'time'          => 'chartype,length,defaultvalue,width,css,required',
        'timerange'     => 'chartype,length,defaultvalue,width,css,required',
        'datetime'      => 'chartype,length,defaultvalue,width,css,required',
        'datetimerange' => 'chartype,length,defaultvalue,width,css,required',
        'select'        => 'chartype,length,defaultvalue,width,required,is_multiple,datasource,options',
        'radio'         => 'chartype,length,defaultvalue,width,datasource,options',
        'checkbox'      => 'chartype,length,defaultvalue,width,datasource,options',
        'image'         => 'chartype,length,defaultvalue,width,css,required,upload_allowext',
        'images'        => 'chartype,length,defaultvalue,width,css,required,upload_allowext,upload_numbers',
        'file'          => 'chartype,length,defaultvalue,width,css,required,upload_allowext',
        'files'         => 'chartype,length,defaultvalue,width,css,required,upload_allowext,upload_numbers',
        'color_select'  => 'chartype,length,defaultvalue,width,css,required',
        'slider'        => 'chartype,length,defaultvalue,width,css,required',
        'score'         => 'chartype,length,defaultvalue,width,css,required',
    ];

    /**
     * 字段属性配置表单项生成
     *
     * @param string $items 字段类型
     * @param array $values  默认值
     * @return void
     */
    public static function getSettingTpl($type='text',$default=[])
    {
        if($type == 'image' || $type == 'images'){
            $default['upload_allowext'] = $default['upload_allowext']??'jpg,gif,bmp,png';
            $default['upload_numbers'] = $type == 'images' ? 10 : 1;
        }
        if($type == 'file' || $type == 'files'){
            $default['upload_allowext'] = $default['upload_allowext']??'doc,docx,xls,xlsx,ppt,pptx,pdf';
            $default['upload_numbers'] = $type == 'files' ? 10 : 1;
        }
        return self::getFormTpl($type,$default);
    }

    /**
     * 自定义字段里面渲染字段设置的额外配置信息
     * @param $type 什么字段类型
     * @param string $default 默认的值
     * @return string
     */
    private static function rendFieldOptions($type, $default = '')
    {
        $data = self::$modelFieldType[$type]['field_list'];
        $str = '<select name="setting[chartype]" class="layui-select" lay-filter="fieldtype_filter" lay-search required>';
        foreach ($data as $val) {
            //如果模型字段可选的数据字段超过2种选择，使用select元素来渲染，如果等于2种选择，则使用radio来进行渲染。
            if ($default == $val) {
                $str .= '<option selected value="' . $val . '">' . $val . '</option>';
            } else {
                $str .= '<option value="' . $val . '">' . $val . '</option>';
            }
        }
        $str .= '</select>';
        return $str;
    }

    /**
     * 自定义字段里面渲染字段设置的额外配置信息
     * @param $type 什么字段类型
     * @param string $default 默认的值
     * @return string
     */
    private static function mkSelect($name,$items='否,是', $default = '')
    {
        $items = is_array($items) ? $items : str2arr($items);
        $str  = '<select name="setting['.$name.']" lay-filter="'.$name.'_filter">';
        foreach ($items as $k=>$v) {
            if(is_array($v)){
                $str  .= '<optgroup label="'.$k.'">';
                foreach ($v as $k1=>$v1) {
                    $value = $k.'@'.$k1;
                    $str  .= '<option value="'.$value.'"'.($value == $default ? 'selected' : '').'>'.$v1.'</option>';
                }
                $str  .= '</optgroup>';
            }else{
                $str  .= '<option value="'.$k.'"'.($k == $default ? 'selected' : '').'>'.$v.'</option>';
            }
        }
        $str  .= '</select>';
        return $str;
    }
    
    private static function getFormTpl($type='text',$default='')
    {
       $fields = str2arr(static::$settingField[$type]);
       $str = '';
       
       foreach ($fields as $field) {
            [$title,$tips,$form] = ['','',''];
            $value = isset($default[$field]) ? $default[$field] : '';
            switch ($field) {
                case 'width':
                    $title = '表单宽度';
                    $value = $value ?? '100';
                    $form  = '<input name="setting[width]" value="'.$value.'" pattern="^\d+$" autocomplete="off" placeholder="请输入表单宽度(单位百分比)" class="layui-input">';
                    break;
                case 'height':
                    $title = '表单高度';
                    $form  = '<input name="setting[height]" value="'.$value.'" pattern="^\d+$" autocomplete="off" placeholder="请输入表单高度(单位px)" class="layui-input">';
                    break;
                case 'css':
                    $title = '表单样式';
                    $value = $value ?? 'layui-input';
                    $form  = '<input name="setting[css]" value="'.$value.'"  pattern="^[A-Za-z0-9_-]+$" placeholder="请输入表单样式class名称" class="layui-input">';
                    break;
                case 'min':
                    $title = '字符最小长度';
                    $value = empty($value) ? 0 : $value;
                    $form  = '<input name="setting[min]" value="'.$value.'"  pattern="^[0-9]+$" placeholder="请输入字符长度取值范围最小值" class="layui-input">';
                    break;
                case 'max':
                    $title = '字符最大长度';
                    $value = empty($value) ? '255' : $value;
                    $form  = '<input name="setting[max]" value="'.$value.'"  pattern="^[0-9]+$" placeholder="请输入字符长度取值范围最大值" class="layui-input">';
                    break;
                case 'chartype':
                    $title = '字段类型';
                    $form  = self::rendFieldOptions($type,$value);
                    break;
                case 'length':
                    $title = '字段长度';
                    $value = empty($value) ? '255' : $value;
                    $form  = '<input name="setting[length]" id="setting_length" value="'.$value.'"  pattern="^[0-9]+$" placeholder="请输入表单字段长度" class="layui-input">';
                    break;
                case 'defaultvalue':
                    $title = '默认值';
                    $form  = '<input name="setting[defaultvalue]" value="'.$value.'" placeholder="请输入表单默认值" class="layui-input">';
                    break;
                case 'upload_allowext':
                    $title = '允许上传的文件格式';
                    $form  = '<input name="setting[upload_allowext]" value="'.$value.'" placeholder="请输入文件格式,英文逗号分割" class="layui-input">';
                    break;
                case 'upload_numbers':
                    $title = '允许上传的文件个数';
                    $form  = '<input name="setting[upload_numbers]" value="'.$value.'" placeholder="请输入允许上传的文件个数" class="layui-input">';
                    break;
                case 'options':
                    $title = '自定义选项';
                    $form  = '<textarea name="setting[options]" id="setting_options" placeholder="选项名称1:选项值1，多个可用“|”分割" class="layui-textarea">'.$value.'</textarea>';
                    break;
                case 'is_password':
                    $title = '是否为密码框';
                    $form  = self::mkSelect('is_password','否,是',$value);
                    break;
                case 'is_multiple':
                    $title = '是否支持多选';
                    $form  = self::mkSelect('is_multiple','否,是',$value);
                    break;
                case 'datasource':
                    $title = '数据来源';
                    $dicts = Library::$sapp->db->name('SystemBase')->where(['type'=>'常用字典'])->column('name','code');
                    $options = [
                        'diy'=>'自定义数据',
                        'module' => config('cms.selectDataModule'),
                        'dict' => $dicts,
                    ];
                    
                    $form  = self::mkSelect('datasource',$options,$value);
                    break;
                case 'required':
                    $title = '是否必填';
                    $form  = self::mkSelect('min','否,是',$value);
                    break;
            }

            $str .= '<div class="layui-col-xs'.($field == 'options' ? 12 : 6).'">';
            $str .= '    <label class="layui-form-label">'.$title.'</label>';
            $str .= '    <div class="layui-input-block">'.$form.'</div>';
            $str .= '</div>';
        }
        $str .= '<script>layui.form.render();';
        $str .= '  layui.form.on("select(fieldtype_filter)",function (data) {';
        $str .= '    var input = $("#setting_length");';
        $str .= '    if(input.length > 0){';
        $str .= "        if(['varchar', 'char'].includes(data.value)){input.val(255);}";
        $str .= "        else if(['int','integer', 'real', 'double', 'float', 'decimal', 'numeric'].includes(data.value)){input.val(11)}";
        $str .= "        else if(['bigint'].includes(data.value)){input.val(20)}";
        $str .= "        else if(['tinyint','smallint','mediumint'].includes(data.value)){input.val(3)}";
        $str .= "        else if(['text', 'tinytext', 'mediumtext', 'longtext'].includes(data.value)){input.val('')}";
        $str .= '    }';
        $str .= ' })</script>';
        return $str;
    }

    /**
     * 获取模型数据
     *
     * @param [type] $mid
     * @return array||void
     */
    public static function getModule($mid,$isSuper=false)
    {
        $module = Library::$sapp->db->name('module_diy_form')->find($mid);
        if($module){
            $listFields = Library::$sapp->db->name('module_diy_field')->where(['typeid'=>$module['id']])->whereRaw('islist = 1')->order('sort asc,id asc')->select()->toArray();
            $searchFields = [];
            if($isSuper){
                $formFields = Library::$sapp->db->name('module_diy_field')->where(['typeid'=>$module['id']])->whereRaw('isshow >= 1')->order('sort asc,id asc')->select()->toArray();
            }else{
                $formFields = Library::$sapp->db->name('module_diy_field')->where(['typeid'=>$module['id'],'isshow'=>1])->order('sort asc,id asc')->select()->toArray();
            }
            
            
            foreach ($listFields as $val) {
                $searchFields[$val['formtype']][] = $val;
            }
            unset($listFields['status']);
            unset($listFields['create_at']);
            //表单字段
            $module['formFields']   = $formFields;
            //列表字段
            $module['listFields']   = $listFields;
            return $module;
        }
        return false;
    }

    /**
     * 静态实例对象
     * @param array $var 实例参数
     * @param boolean $new 创建新实例
     * @return static|mixed
     */
    public static function instance(array $var = [], bool $new = false)
    {
        return \think\Container::getInstance()->make(static::class, $var, $new);
    }

    /**
     * 数据库文件路径
     *
     * @return void
     */
    protected static function getPath()
    {
        return root_path() . 'database' . DIRECTORY_SEPARATOR . 'migrations';
    }

    /**
     * 获取数据表信息，不存在会自动创建
     *
     * @param string $table
     * @param string $comment
     * @return object
     */
    private static function getTable($table='',$comment='')
    {
        $migration = new Migrator('default',0);
        $default = Library::$sapp->config->get('database.default');
        $config = Library::$sapp->config->get("database.connections.{$default}");
        $options = [
            'adapter'      => $config['type'],
            'host'         => $config['hostname'],
            'name'         => $config['database'],
            'user'         => $config['username'],
            'pass'         => $config['password'],
            'port'         => $config['hostport'],
            'charset'      => $config['charset'],
            'suffix'       => $config['suffix'] ?? '',
            'table_prefix' => $config['prefix'],
        ];

        $adapter = \Phinx\Db\Adapter\AdapterFactory::instance()->getAdapter($options['adapter'], $options);

        if ($adapter->hasOption('table_prefix') || $adapter->hasOption('table_suffix')) {
            $adapter = \Phinx\Db\Adapter\AdapterFactory::instance()->getWrapper('prefix', $adapter);
        }
        $migration->setAdapter($adapter);
        if($table){
            if(!$migration->hasTable($table)){
                $migration->table($table, [
                    'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => $comment,
                ])->create();
            }
            return $migration->table($table);
        }
        return $migration;
    }

    /**
     * 设置字段信息
     *
     * @param [type] $table 数据表名
     * @param array $field 字段属性
     * @return void
     */
    public static function setField($table,$column=[])
    {
        $tableObj = self::getTable($table);
        $action   = $tableObj->hasColumn($column['name']) ? 'changeColumn' : 'addColumn';
        $attrs    = ['default' => $column['default'], 'null' => $column['null'], 'comment' => $column['comment']];
        $type     = strtolower($column['type']);
        // halt($column['name'],$type,$attrs);
        if($type == 'bigint'){
            $type      = 'biginteger';
            $attrs['limit']   = !empty($column['length']) ? $column['length'] : 20;
            $attrs['default'] = !empty($attrs['default']) ? $attrs['default'] :  0;
        }elseif($type == 'int'){
            $type      = 'integer';
            $attrs['limit']   = !empty($column['length']) ? $column['length'] : 11;
            $attrs['default'] = !empty($attrs['default']) ? $attrs['default'] :  0;
        }elseif($type == 'tinyint'){
            $type      = 'tinyint';
            $attrs['limit']   = 3;
            $attrs['default'] = !empty($attrs['default']) ? $attrs['default'] :  0;
        }elseif($type == 'decimal' || $type == 'float'){
            $attrs['scale']   = 2;
            $attrs['default'] = !empty($attrs['default']) ? $attrs['default'] : '0.00';
        }elseif($type == 'longtext'){
            $type    = 'text';
            $attrs['limit'] = '4294967295';
            unset($attrs['default']);
        }elseif($type == 'text'){
            unset($attrs['default']);
        }elseif($type == 'varchar' || $type == 'char'){
            $type    = 'string';
            $attrs['limit'] = !empty($column['length']) ? $column['length'] : 255;
        }elseif($type == 'timestamp' || $type == 'datetime'){
            $attrs['default'] = null;
        }
        return $tableObj->$action($column['name'],$type,$attrs)->save();
    }
    /**
     * 检查并创建字段
     *
     * @param [type] $mid
     * @param [type] $fields
     * @return void
     */
    public static function createField($mid,$fields)
    {
        $module    = Library::$sapp->db->name('module_diy_form')->find($mid);
        $table     = $module['tablename'];
        // 1、格式化字段属性
        $setting = json_decode($fields['setting'], true);
        self::setField($table,[
            'name'    => $fields['field'],
            'type'    => $setting['chartype'],
            'comment' => $fields['title'],
            'null'    => isset($setting['min']) && $setting['min'] > 0 ? false : true,
            'length'  => $setting['length'] ?? 255,
            'default' => $setting['defaultvalue']??''
        ]);
        return true;
    }

}